/**
 * DataTableList 0.2.2
 *
 * http://code.google.com/p/datatablelist
 *
 * Copyright Intelygenz Software.
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * Date: 27-04-2011
 *
 * This class manage a collection of DataTables, which are composed by an array with the 
 * column names and an array of arrays with the row values.
 * 
 * [version] upd|del|dep|new|fix - description
 *
 * [0.2.3] new - DataTable.ToObject() return a JSON data array
 * [0.2.2] fix - DataTableRow.Value() can return values of type Array
 * [0.2.1] fix - Deleted unused param p_colName in DataTableRow.ToObject()
 * 		   upd - Code refactoring for correct autocompletion in Eclipse
 * [0.2.0] new - DataTable.Set(p_row, p_index) : replace row in position "p_index" with "p_row"
 * 		   upd - _Debug and _Error support infinite arguments
 * 		   del - DataTableRow ToJson, only ToObject is available
 * 		   upd - Cols, Rows and Meta fields in DataTable object are now public
 * [0.1.9] fix - Find: fix when {"returnIndexes":true}
 * [0.1.8] new - Parse: convert JsonDataTable or DataTable object to DataTableList
 * [0.1.7] fix - DataTable: Value
 * [0.1.6] new - DataTableRow: Value, add setter for change value
 * [0.1.5] new - added new methods to DataTable: AddRow, Last. update Get method of DataTable
 * [0.1.4] fix - When DataTableRow Value it was a son DataTable and it had not meta, the rows always came empty
 * [0.1.3] new - Added error control in modern browsers console
 * [0.1.2] new - Meta - Added managing "meta" values on DataTableList, DataTable and DataTableRow
 * [0.1.1] new - Debug - Added console contextualized logging
 * [0.1.0] new - Settings - Allow define "LabelColumns", "LabelRows" and "LabelMeta" names
 * [0.0.9] new - RemoveIndexes - Remove rows indicated in a indexes array
 * 		   new - Find - Added new option returnIndexes that indicates if de return its an Array of indexes of find it
 *	 	   fix - IE compatibility for indexOfColumn, Array.indexOf not working in IE with the old fix. mortas.
 * [0.0.8] fix - IE compatibility form Array.indexOf
 * [0.0.7] update - first stable version
 * 
 */

var jsdtl = {};

//json data table list
//
(function () {
	// private context
	//
	var _Settings = { 
		 "JsonCols" : "cols" 
		,"JsonMeta" : "meta"
		,"JsonRows" : "rows"
	};

	function _Debug (){
		if ( jsdtl.Debugging && window.console && window.console.log ) {
			window.console.log("[JsDataTableList]", arguments);
		}
	};
	
	function _Error (){
		if (window.console && window.console.log){
			window.console.error("[JsDataTableList]", arguments);
		}
	};

	//data table list
	//
	function _DataTableList (p_json) {
		// private context
		//
		_Debug( "Create DataTableList from Json ", p_json );
		
		var _Dtl = this;
		
		var _Cols  = p_json[_Settings["JsonCols"]];
		var _Lists = p_json;

		_Debug( "DataTableList Cols & Lists", _Cols, _Lists );

		// public context
		//
		this.GetCols = function (){
			return _Cols;
		};

		this.GetList = function (p_listName){
			return _List[p_listName];
		};
		
		this.DataTable = function (p_listName) {
			var cols = _Cols[p_listName];
			if(!cols) {
				_Error( "Column ["+p_listName+"] not found", _Cols );
			}
			var rows = _Lists[p_listName];
			if(!rows) {
				_Error( "List ["+p_listName+"] not found", _Lists );
			}
			var meta = {};
			if ( rows && rows.length>0 ){
				if ( typeof rows[0] == "object" && !rows[0].length){
					meta = rows.shift();
				}
			}

			return new _DataTable( 
				{ "cols" : cols
				, "meta" : meta
				, "rows" : rows
				}
				, _Dtl
			);
		};
	};

	//data table
	//
	function _DataTable (p_json, p_dtl) {
		// private context
		//
		var _DataTableList = p_dtl;
		

		//public context
		//
		this.Cols = p_json[_Settings["JsonCols"]];
		this.Meta = p_json[_Settings["JsonMeta"]];
		this.Rows = p_json[_Settings["JsonRows"]];
		
		_Debug( "DataTable", "cols", this.Cols, "meta", this.Meta, "rows", this.Rows );

		this.DataTableList = function () {
			return _DataTableList;
		};

		this.GetCols = function () {
			return this.Cols;
		};

		this.GetMeta = function () {
			return this.Meta;
		};
		
		this.IndexOfColumn = function ( p_columnName ) {
			return this.Cols.indexOf( p_columnName );
		};
		
		this.Value = function ( p_columnName, p_index, p_columnValue ) {
			if(!p_index) p_index = 0;
			var row = this.Get(p_index);
			return row.Value(p_columnName, p_columnValue);
		};
		
		this.Set = function ( p_row, p_index ) {
			this.Rows.splice( p_index, 1, p_row );
		};
		
		this.Get = function ( p_index, p_remove ) {
			if(!p_index) p_index = 0;
			var dtr = new _DataTableRow(this, this.Rows[p_index], p_index);
			if ( p_remove ){
				this.Rows.splice( p_index, 1 );
			}
			return dtr;
		};

		this.Each = function ( f_func ) {
			if ( this.Rows ) {
				var rowsC, rowsK = this.Rows.length;
			
				for ( rowsC=0; rowsC<rowsK ; rowsC++ ) {
					if( f_func( this.Get(rowsC) ) == false ){
						break;
					};
				}
			}
		};
		
		this.Size = function () {
			return this.Rows ? this.Rows.length : 0;
		};
		
		this.GetColValues = function (p_colName){
			var values = [];
			if ( this.Rows ) {
				var index = this.IndexOfColumn(p_colName);			
				var rowsC, rowsK = this.Rows.length;			
				for ( rowsC=0; rowsC<rowsK ; rowsC++ ) {
					values[values.length] = this.Rows[rowsC][index];
				}
			}
			return values;
		};
		
		this.RemoveIndexes = function (p_indexes){
			var rows = this.Rows;
			var f, F=p_indexes.length;
			for ( f=0;f<F;f++ ){
				rows.splice(p_indexes[f], 1);
			}
			this.Rows = rows;
		};

		this.Find = function( p_dataTableColNameIf, p_dataTableColValueIf, p_options ){
			var indexes = [];
			var rowC    = 0;
			var rows    = [];
			var options = { "all" : false, "ignoreCase" : false, "like" : false, "returnIndex" : false };

			if ( p_options ) {
				for( var p in p_options) {
					options[p] = p_options[p];
				}
			}

			if ( options["ignoreCase"] ) {
				if ( options["like"] ) {
					rowC = 0;
					this.Each (					// ignoreCase && like
						function ( p_dataTableRow ) {
							if ( p_dataTableRow.Value(p_dataTableColNameIf).toUpperCase().indexOf( p_dataTableColValueIf.toUpperCase() ) >-1 ) {
								indexes[indexes.length] = rowC;
								rows[rows.length] = p_dataTableRow.Row;
								if(!options["all"]) return false;		// returns only the first finding, breaking the loop
							}
							rowC++;
							return true;
						}
					);
				}
				else {
					rowC = 0;
					this.Each (					// ignoreCase && equals
						function ( p_dataTableRow ) {
							if ( p_dataTableRow.Value(p_dataTableColNameIf).toUpperCase() == p_dataTableColValueIf.toUpperCase() ) {
								indexes[indexes.length] = rowC;
								rows[rows.length] = p_dataTableRow.Row;
								if(!options["all"]) return false;		// returns only the first finding, breaking the loop
							}
							rowC++;
							return true;
						}
					);
				}
			}
			else {
				if ( options["like"] ) {
					rowC = 0;
					this.Each (					// caseSensitive && like
						function ( p_dataTableRow ) {
							if ( p_dataTableRow.Value(p_dataTableColNameIf).indexOf( p_dataTableColValueIf ) > -1 ) {
								indexes[indexes.length] = rowC;
								rows[rows.length] = p_dataTableRow.Row;
								if(!options["all"]) return false;			// returns only the first finding, breaking the loop
							}
							rowC++;
							return true;
						}
					);
				}
				else {
					rowC = 0;
					this.Each (					// caseSensitive && equals
						function ( p_dataTableRow ) {
							if ( p_dataTableRow.Value(p_dataTableColNameIf) == p_dataTableColValueIf ) {
								indexes[indexes.length] = rowC;
								rows[rows.length] = p_dataTableRow.Row;
								if(!options["all"]) return false;		// returns only the first finding, breaking the loop
							}
							rowC++;
							return true;
						}
					);
				}
			}

			return ( options["returnIndex"] ? 
						indexes 
						: 
						new _DataTable (
							{ "cols" : this.Cols 
							, "rows" : rows
							, "meta" : this.Meta
							}
							, this.MyDataTableList
						)
					);
		};

		this.AddRow = function (p_row){
			this.Rows.push( p_row );
		};

		this.Last = function (p_remove){
			return this.Get( this.Size()-1, p_remove);
		};
		
		this.HasCol = function (p_colName){
			return this.Cols.indexOf(p_colName)==-1 ? false : true;
		};
		
		this.ToObject = function () {
			var dataArray = [];
			this.Each(function (p_row){
				dataArray[dataArray.length] = p_row.ToObject();
			});
			return dataArray;
		};
	}

	//data table row
	//
	function _DataTableRow (p_dt, p_row, p_index) {
		//private context
		//
		var _Dt  = p_dt;
		
		//public context
		//
		this.Row   	   = p_row;
		this.Index 	   = p_index;

		this.Value = function (p_colName, p_value) {
			if ( p_value ){
				this.Row[ _Dt.IndexOfColumn(p_colName) ] = p_value;
			}
			var value = this.Row[ _Dt.IndexOfColumn(p_colName) ];
			
			var isArray = value instanceof Array;
			
			_Debug( "DataTableRow Value (" + p_colName + ")", value, typeof value, isArray );
			
			if( value!=null && typeof value == "object" && !isArray){
				var cols = _Dt.DataTableList().GetCols()[p_colName];
				if(!cols) {
					_Error( "Column ["+p_colName+"] not found", _Dt.GetCols() );
				}
				var rows = value[p_colName];
				if(!rows) {
					_Error( "List ["+p_colName+"] not found", value );
				}
				var meta = {};
				if ( rows && rows.length>0 ){
					if ( typeof rows[0] == "object" && !rows[0].length ){
						meta = rows.shift();
					}
				}

				_Debug( "DataTableRow creating child DataTable", cols, meta, rows );

				value = new _DataTable (
					{ "cols" : cols
					, "meta" : meta
					, "rows" : rows
					}
					, _Dt.DataTableList()
				);

				_Debug( "DataTableRow created with "+value.Size()+" childs" );

			}

			return value;
		};

		this.HasCol = function (p_colName){
			return _Dt.HasCol(p_colName);
		};

		this.GetCols = function (){
			return _Dt.GetCols();
		};
		
		this.GetMeta = function (){
			return _Dt.GetMeta();
		};

		this.GetDataTable = function (){
			return new _DataTable (
				{ "cols" : _Dt.GetCols() 
				, "rows" : [this.Row]
				, "meta" : _Dt.GetMeta()
				}
				, _Dt.DataTableList()
			);
		};

		this.ToObject = function () {
			var data = {};
			var columnNames = _Dt.GetCols();
			var f, F = columnNames.length;
			for ( f = 0; f < F; f++ ) {
				data[ columnNames[f] ] = this.Value( columnNames[f] );
			}
			return data;
		};

		this.ToJson = this.ToObject;
		
	};
	
	// Compatibility for Array.indexOf
	//
	if(!Array.prototype.indexOf){Array.prototype.indexOf=function(obj){var i,I=this.length; for(i=0;i<I;i++){if(this[i]==obj){return i;}} return-1;};};

	// public interfaces
	//
	
	jsdtl.Debugging = false;
	
	jsdtl.Settings = function (p_label, p_value){
		_Settings[p_label] = p_value;
	};
	
	jsdtl.DataTableList = function (p_json) {
		return new _DataTableList(p_json);
	};
	
	jsdtl.DataTable = function(p_json){
		return new _DataTable(p_json);
	};

	jsdtl.Parse = function (p_dtlOrdt){
		if ( p_dtlOrdt["rows"] ){
			return this.DataTable(p_dtlOrdt);
		}	
		else if ( p_dtlOrdt["cols"] ) {
			return p_dtlOrdt;
		}
		else {
			_Error("Parse: Is not valid format");
		}
	};

})();
